﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Qreed.Drawing.Drawing2D
{
    /// <summary>
    /// Represents a line defined by two <see cref="PointF"/> objects.
    /// </summary>
    public class Line2D
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Line2D"/> class.
        /// </summary>
        public Line2D()
        {}

        /// <summary>
        /// Initializes a new instance of the <see cref="Line2D"/> class.
        /// </summary>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        public Line2D(PointF start, PointF end)
        {
            Start = start;
            End = end;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Line2D (Start: {0}, End: {1})", Start, End);
        }

        private PointF _start;
        /// <summary>
        /// Gets or sets the start.
        /// </summary>
        /// <value>The start.</value>
        public PointF Start
        {
            get { return this._start; }
            set { this._start = value; }
        }

        private PointF _end;
        /// <summary>
        /// Gets or sets the end.
        /// </summary>
        /// <value>The end.</value>
        public PointF End
        {
            get { return this._end; }
            set { this._end = value; }
        }

        /// <summary>
        /// Gets the vector.
        /// </summary>
        /// <value>The vector.</value>
        public PointF GetVector()
        {
            return new PointF(End.X - Start.X, End.Y - Start.Y);
        }

        /// <summary>
        /// Gets the length.
        /// </summary>
        /// <returns></returns>
        public float GetLength()
        {
            PointF v = GetVector();
            return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y);
        }

        /// <summary>
        /// Gets the point distance to this line.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <returns>The shortest distance to the line.</returns>
        public float GetPointDistance(PointF point)
        {
            // d = (| vLine x (p-o) |) / |vLine|
            // Where p is a vector containing the point, a is the origin of the line, and b is the vector of the line.

            PointF vLine = GetVector();
            float length = GetLength();

            PointF origin = Start;
            


            return 0.0f;
        }


        /// <summary>
        /// Locates the intersection point.
        /// </summary>
        /// <param name="other">The other line.</param>
        /// <param name="includeStartEndPoint">if set to <c>true</c> the method will include start and end points.</param>
        /// <returns>
        /// The intersection coordinates or <c>null</c> if the lines do not intersect.
        /// </returns>
        /// <remarks>http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/</remarks>
        public PointF? GetIntersectionPoint(Line2D other, bool includeStartEndPoint)
        {
            if (includeStartEndPoint)
            {
                if (this.Start == other.Start || this.Start == other.End)
                    return this.Start;
                else if (this.End == other.End || this.End == other.Start)
                    return this.End;
            }

            PointF vector = GetVector();
            PointF otherVector = other.GetVector();

            double denominator = otherVector.Y * vector.X - otherVector.X * vector.Y;

            // If the denominator is 0 the lines are parallel

            if (Math.Abs(denominator) > float.Epsilon)
            {
                float x1 = this.Start.X;
                float y1 = this.Start.Y;
                float x2 = this.End.X;
                float y2 = this.End.Y;
                float x3 = other.Start.X;
                float y3 = other.Start.Y;
                float x4 = other.End.X;
                float y4 = other.End.Y;

                float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / (float)denominator;
                float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / (float)denominator;

                if ((ua > 0.0f) && (ua < 1.0f) && (ub > 0.0f) && (ub < 1.0f))
                {
                    // x = x1 + ua (x2 - x1)
                    // y = y1 + ua (y2 - y1) 

                    return new PointF(x1 + ua * (x2 - x1), y1 + ua * (y2 - y1));
                }
            }

            return null;
        }
    }
}
